from collections import defaultdict
from datetime import datetime, timedelta
from typing import Tuple
import json
import os
import numpy
from pandas import DataFrame

import threading


class _UuidToken:

    def __init__(self):
        self.uuid_token = 0
        self._uuid_token_lock = threading.Lock()

    def gen_uuid(self) -> str:
        time = datetime.now()
        with self._uuid_token_lock:
            self.uuid_token += 1
            token = self.uuid_token
        return f"{time.year}{time.month:02d}{time.day:02d}{time.hour:02d}{time.minute:02d}{time.second:02d}{token}"

class utils:

    _uuid_generator = _UuidToken()

    @staticmethod
    def next_uuid()->str:
        """
        生成唯一的uuid。
        """
        return utils._uuid_generator.gen_uuid()

    @staticmethod
    def std(value,max,min):
        """
        标准化值.
        """
        assert value <= max
        assert value >= min
        if max == min:
            return 1
        uint =  1 / (max - min)
        return uint * ( value-min)


    @staticmethod
    def split_datetime(start:datetime,end:datetime,batch_day:int):
        """
        分割一段时间，以便分批处理。
        返回:
            [[start1,end1],[start2,end2]...]
        """
        assert batch_day > 0
        batch_start = start
        time_span_list = []
        while batch_start.__lt__(end):
            batch_end = batch_start + timedelta(days=(batch_day-1))
            batch_end = utils.to_end_date(batch_end)
            if (batch_end.__gt__(end)):
                batch_end = end
            assert batch_start<=batch_end
            time_span_list.append([batch_start,batch_end])
            batch_start = utils.to_start_date(batch_end + timedelta(days = 1))
            assert batch_start>=batch_end
        assert time_span_list[-1][-1] == end
        assert time_span_list[0][0] == start
        return time_span_list

    @staticmethod
    def is_same_day(d1: datetime, d2: datetime) -> bool:
        return d1.day == d2.day and d1.month == d2.month and d1.year == d2.year

    @staticmethod
    def is_same_minitue(d1: datetime, d2: datetime) -> bool:
        return utils.is_same_day(d1, d2) and d1.hour == d2.hour and d1.minute == d2.minute

    @staticmethod
    def is_same_time(d1: datetime, d2: datetime,deltaMinitue:int = 0,deltaSecond:int = 0) -> bool:
        deltaMinitue = abs(deltaMinitue)
        deltaSecond = abs(deltaSecond)
        start = d1 - timedelta(minutes=deltaMinitue) -  timedelta(seconds=deltaSecond)
        if(d2 < start):
            return False
        end = d1 + timedelta(minutes=deltaMinitue) +  timedelta(seconds=deltaSecond)
        if d2 > end:
            return False
        return True

    @staticmethod
    def isEqualInt(v1:int,v2:int,delta:int)->bool:
        delta = abs(delta)
        if v2 < v1 - delta:
            return False
        if(v2 > v1 + delta):
            return False
        return True

    @staticmethod
    def isEqualFloat(v1: float, v2: float, delta: float) ->bool:
        delta = abs(delta)
        if v2 < v1 - delta:
            return False
        if (v2 > v1 + delta):
            return False
        return True

    @staticmethod
    def keep_3_float(value:float) -> float:
        return int(value * 1000) / 1000

    @staticmethod
    def to_start_date(d: datetime) -> datetime:
        return datetime(year=d.year, month=d.month, day=d.day, hour=00, minute=00, second=00)

    @staticmethod
    def to_end_date(d: datetime) -> datetime:
        return datetime(year=d.year, month=d.month, day=d.day, hour=23, minute=59, second=59)

    """
    展开参数map，如下
      originParams = {
          'wwf':[1,None,5],
          'zx':['sd',None,'dd']
      }
      将originParams展开为列表模式。
      {'wwf': 1, 'zx': 'sd'}
      {'wwf': 1, 'zx': None}
      {'wwf': 1, 'zx': 'dd'}
      {'wwf': None, 'zx': 'sd'}
      {'wwf': None, 'zx': None}
      {'wwf': None, 'zx': 'dd'}
      {'wwf': 5, 'zx': 'sd'}
      {'wwf': 5, 'zx': None}
      {'wwf': 5, 'zx': 'dd'}
      """
    def expandParamsMap(params: {})->[]:
        paramList = []
        utils.__expandParamMapList(paramList, params, {}, list(params.keys()), 0)
        return paramList

    def __expandParamMapList(list: [], originParams: {}, param: {}, keyList: [], index):
        size = len(keyList)
        if index >= size:
            list.append(param.copy())
            return
        key = keyList[index]
        values = originParams[key]
        for value in values:
            param[key] = value
            utils.__expandParamMapList(list, originParams, param, keyList, index + 1)

    @classmethod
    def changeTime(cls, dt:datetime, year:int =None,month = None, day = None, hour:int = None,minute:int = None,second = None):
        if year is None:
            year = dt.year
        if month is None:
            month = dt.month
        if day is None:
            day = dt.day
        if hour is None:
            hour = dt.hour
        if minute is None:
            minute = dt.minute
        if second is None:
            second = dt.second

        return datetime(year=year,month=month,day=day,hour=hour,minute=minute,second=second)

    @classmethod
    def pnl(cls,close_price:float,open_price:float):
        return 100 * (close_price - open_price) / open_price


file_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir))

class Properties:
    # 文件的相对路径
    file_url: str
    # 文件编码
    encode: str

    def __init__(self, file_url, encode='utf8'):
        self.file_url = file_url
        self.encode =encode

    def get_properties(self)->{}:
        try:
            pro_file = open(self.file_url, 'r', encoding=self.encode)
            pro = {}
            for line in pro_file:
                if line.find('=') > 0:
                    s = line.replace('\n', '').split("=")
                    pro[s[0].strip()] = s[1].strip()
        except Exception as e:
            raise e
        else:
            pro_file.close()
        return pro

# 获取配置文件的配置信息[ 绝对路径，在docker中的使用 ]
# def get_config()->dict:
#     file_name = '/config/config.properties'
#     return PropertiesUtils(file_url=file_path + file_name).get_properties()
